// Copyright (c) 1996-2002 John Lyon-Smith. All rights reserved.

#pragma once

#include <wxtl\Base.h>

using namespace boost;

namespace wxtl
{
	// Base 64 encode/decode - useful for storing binary data in XML. See RFC 1521 for general idea
	inline void _stdcall encode64(const BYTE* pData, DWORD len, shared_array<char>& pEncoded)
	{
		static const char s_achBase64Codes[] =
		{
			'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 
			'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 
			'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 
			'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 
			'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 
			'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 
			'w', 'x', 'y', 'z', '0', '1', '2', '3', 
			'4', '5', '6', '7', '8', '9', '+', '/'
		};

		DWORD extra = len % 3;
		DWORD roundDown = len - extra;
		DWORD resultLen = ((len + 2) / 3) * 4;
		
		pEncoded = shared_array<char>(new char[resultLen + 1]);
		
		LPSTR pch = pEncoded.get();
		DWORD j = 0;
		BYTE a, b, c;
		DWORD i = 0;

		while (i < roundDown)
		{
			a = pData[i++];
			pch[j++] = s_achBase64Codes[(a & 0xfc) >> 2];

			b = pData[i++];
			pch[j++] = s_achBase64Codes[((a & 0x03) << 4) | ((b & 0xf0) >> 4)];

			c = pData[i++];
			pch[j++] = s_achBase64Codes[((b & 0x0f) << 2) | ((c & 0xc0) >> 6)];

			pch[j++] = s_achBase64Codes[(c & 0x3f)];
		}

		switch (extra)
		{
		case 0:
			break;
		
		case 1:
			a = pData[i++];
			pch[j++] = s_achBase64Codes[(a & 0xfc) >> 2];

			b = 0;
			pch[j++] = s_achBase64Codes[((a & 0x03) << 4) | ((b & 0xf0) >> 4)];

			pch[j++] = '=';
			pch[j] = '=';
			break;
		
		case 2:
			a = pData[i++];
			pch[j++] = s_achBase64Codes[(a & 0xfc) >> 2];

			b = pData[i];
			pch[j++] = s_achBase64Codes[((a & 0x03) << 4) | ((b & 0xf0) >> 4)];

			c = 0;
			pch[j++] = s_achBase64Codes[((b & 0x0f) << 2) | ((c & 0xC0) >> 6)];

			pch[j] = '=';
			break;
		}
		
		pEncoded[resultLen] = 0;
	}

	inline void _stdcall decode64(const char* pData, shared_array<BYTE>& pDecoded, DWORD& len)
	{
		static BYTE s_abyRevBase64Codes[] =
		{
			// Non-printable ASCII
			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 

			// Printable ASCII
			0xff /* ' ' */, 0xff /* '!' */, 0xff /* '"' */, 0xff /* '#' */, 
			0xff /* '$' */, 0xff /* '%' */, 0xff /* '&' */, 0xff /* ''' */,
			0xff /* '(' */, 0xff /* ')' */, 0xff /* '*' */, 0x3e /* '+' */,
			0xff /* ',' */, 0xff /* '-' */, 0xff /* '.' */, 0x3f /* '/' */,
			0x34 /* '0' */, 0x35 /* '1' */, 0x36 /* '2' */, 0x37 /* '3' */,
			0x38 /* '4' */, 0x39 /* '5' */, 0x3a /* '6' */, 0x3b /* '7' */,
			0x3c /* '8' */, 0x3d /* '9' */, 0xff /* ':' */, 0xff /* ';' */,
			0xff /* '<' */, 0x00 /* '=' */, 0xff /* '>' */, 0xff /* '?' */, 
			0xff /* '@' */, 0x00 /* 'A' */, 0x01 /* 'B' */, 0x02 /* 'C' */,
			0x03 /* 'D' */, 0x04 /* 'E' */, 0x05 /* 'F' */, 0x06 /* 'G' */,
			0x07 /* 'H' */, 0x08 /* 'I' */, 0x09 /* 'J' */, 0x0a /* 'K' */, 
			0x0b /* 'L' */, 0x0c /* 'M' */, 0x0d /* 'N' */, 0x0e /* 'O' */, 
			0x0f /* 'P' */, 0x10 /* 'Q' */, 0x11 /* 'R' */, 0x12 /* 'S' */, 
			0x13 /* 'T' */, 0x14 /* 'U' */, 0x15 /* 'V' */, 0x16 /* 'W' */, 
			0x17 /* 'X' */, 0x18 /* 'Y' */, 0x19 /* 'Z' */, 0xff /* '[' */, 
			0xff /* '\' */, 0xff /* ']' */, 0xff /* '^' */, 0xff /* '_' */, 
			0xff /* '`' */, 0x1a /* 'a' */, 0x1b /* 'b' */, 0x1c /* 'c' */, 
			0x1d /* 'd' */, 0x1e /* 'e' */, 0x1f /* 'f' */, 0x20 /* 'g' */, 
			0x21 /* 'h' */, 0x22 /* 'i' */, 0x23 /* 'j' */, 0x24 /* 'k' */, 
			0x25 /* 'l' */, 0x26 /* 'm' */, 0x27 /* 'n' */, 0x28 /* 'o' */, 
			0x29 /* 'p' */, 0x2a /* 'q' */, 0x2b /* 'r' */, 0x2c /* 's' */, 
			0x2d /* 't' */, 0x2e /* 'u' */, 0x2f /* 'v' */, 0x30 /* 'w' */, 
			0x31 /* 'x' */, 0x32 /* 'y' */, 0x33 /* 'z' */, 0xff /* '{' */, 
			0xff /* '|' */, 0xff /* '}' */, 0xff /* '~' */, 

			// Non-printable ASCII
			0xff
		};
		DWORD stringLen = (DWORD)strlen(pData);
		
		len = (stringLen / 4) * 3;  // Any odd characters will be ignored

		if (len == 0)
			return;

		BYTE* p = new BYTE[len];

		DWORD i = 0;
		DWORD j = 0;
		BYTE a, b, c, d;
		
		while (i < stringLen)
		{
			a = s_abyRevBase64Codes[pData[i++]];
			
			b = s_abyRevBase64Codes[pData[i++]];
			p[j++] = ((a & 0x3f) << 2) | ((b & 0xf0) >> 4);

			c = s_abyRevBase64Codes[pData[i++]];
			p[j++] = ((b & 0x0f) << 4) | ((c & 0xfc) >> 2);

			d = s_abyRevBase64Codes[pData[i++]];
			p[j++] = ((c & 0x03) << 6) | (d & 0x3f);
		}		

		if (pData[stringLen - 1] == '=')
		{
			if (pData[stringLen - 2] == '=')
				len -= 2;
			else 
				len -= 1;
		}

		pDecoded = shared_array<BYTE>(p);
	}
}